package cn.itsource.service.impl;

import cn.dev33.satoken.stp.StpUtil;
import cn.itsource.api.RuleStartRemoteApi;
import cn.itsource.constants.*;
import cn.itsource.domain.dto.DriverSettingRedisDto;
import cn.itsource.dto.RuleDto;
import cn.itsource.exception.GlobalException;
import cn.itsource.mapper.OrderBillMapper;

import cn.itsource.pojo.domain.Login;
import cn.itsource.pojo.domain.Order;
import cn.itsource.mapper.OrderMapper;
import cn.itsource.pojo.domain.OrderBill;
import cn.itsource.pojo.dto.GeoDriverDto;
import cn.itsource.pojo.dto.OrderDto;
import cn.itsource.pojo.dto.PushDriverOrderMsgDto;
import cn.itsource.result.JSONResult;
import cn.itsource.service.IOrderService;
import cn.itsource.utils.CodeGenerateUtils;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.apache.rocketmq.spring.core.RocketMQTemplate;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.geo.*;
import org.springframework.data.redis.connection.RedisGeoCommands;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.messaging.Message;
import org.springframework.messaging.support.MessageBuilder;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.text.DecimalFormat;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;

/**
 * <p>
 * 订单表 服务实现类
 * </p>
 *
 * @author yh
 * @since 2024-09-30
 */
@Service
public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order> implements IOrderService {


    @Autowired
    private RuleStartRemoteApi ruleStartRemoteApi;
    @Autowired
    private OrderBillMapper orderBillMapper;
    @Autowired
    private RedisTemplate<String,Object> redisTemplate;
    @Autowired
    private RocketMQTemplate rocketMQTemplate;
//    @Autowired
//    private DefaultMQPullConsumer pullConsumer;
//    @Autowired
//    private RedissonClient redissonClient;


    @Override
    public JSONResult cancelCustomer(String orderNo) {
        //手动取消订单

        //只需要判断当前订单是否已经被司机接
        Order one = super.getOne(new LambdaQueryWrapper<Order>()
                .eq(Order::getOrderNo,orderNo));
        if(one == null){
            return JSONResult.success();
        }

        if(one.getStatus().equals(RConst.Num.INT_0) ){//没有人接单
            one.setStatus(Constants.orderStatus.ORDER_STATUS_CUSTOMER_CANCEL_ORDER);
            one.setUpdateTime(new Date());
            super.updateById(one);
        }else if(one.getStatus().equals(Constants.orderStatus.ORDER_STATUS_ACCEPTED)) {
            //以下就是
            // 可以收取手续费
            // 可以扣减信誉度
            one.setStatus(Constants.orderStatus.ORDER_STATUS_CUSTOMER_CANCEL_ORDER);
            one.setUpdateTime(new Date());
            super.updateById(one);
        }
        return JSONResult.success();
    }

    @Override
    public JSONResult save(OrderDto orderDto) {
        //要保证 同一个用户一次只能下一个单
        Order one = super.getOne(new LambdaQueryWrapper<Order>()
                .eq(Order::getCustomerId, StpUtil.getLoginIdAsLong())
                .in(Order::getStatus, Constants.orderStatus.getRunOrderStatus()));
        if(one!=null){
            return JSONResult.error("你有订单正在运行");
        }
        long loginId = StpUtil.getLoginIdAsLong();
        String no = CodeGenerateUtils.generateOrderSn(loginId);
        //保存订单
        Order order = saveOrder(orderDto, no, loginId);
        //初始化账单
        initOrderBill(order);
        //去匹配司机
        List<GeoDriverDto> drivers = getDrivers(orderDto);
        //根据司机的id找符合配置司机
        List<GeoDriverDto> driversSetting = getDriversSetting(drivers, order);
        //发送消息
        pushOrderMessage(driversSetting,order);//为什么要使用MQ

        //发送消息
        return JSONResult.success(no);
    }


    /**
     * 寻找司机
     *      递归  内存异常 或者是 栈溢出
     *
     * @param orderDto
     */
    public List<GeoDriverDto> getDrivers(OrderDto orderDto){
        //得到乘客开始位置的经纬度
        // 起始点 //得到司机
        Point point = new Point(Double.valueOf(orderDto.getStartPlaceLongitude()),
                Double.valueOf(orderDto.getStartPlaceLatitude()));
        //设置半径
        Circle circle = new Circle(point, new Distance(RConst.Num.BASE_LONG_2, Metrics.KILOMETERS));
        //构建查询参数
        RedisGeoCommands.GeoRadiusCommandArgs args =
                RedisGeoCommands.GeoRadiusCommandArgs.newGeoRadiusArgs();
        args.includeDistance()//结果中包含 距离
                .includeCoordinates()//结果中包含坐标
                .sortAscending()//由近到远
                .limit(RConst.Num.BASE_LONG_5);//匹配多少个
        //得到范围类的司机 多个
        GeoResults<RedisGeoCommands.GeoLocation<Object>> radius =
                redisTemplate.opsForGeo().radius(CacheConstants.DRIVER_POINT, circle, args);
        //得到list集合
        List<GeoResult<RedisGeoCommands.GeoLocation<Object>>> content = radius.getContent();
        //取司机的id
        return content.stream()
                .map(item ->
                        new GeoDriverDto(Long.valueOf(item.getContent().getName().toString()),
                                item.getDistance().getValue(),
                                item.getDistance().getUnit())
                ).collect(Collectors.toList());
    }

    /**
     * 根据司机的id找符合配置司机
     * @param drivers
     */
    private List<GeoDriverDto> getDriversSetting(List<GeoDriverDto> drivers,Order order) {
        List<GeoDriverDto> driverIds = new ArrayList<>();
        for (GeoDriverDto dto : drivers) {
            //判断司机是否还在线
            Boolean b = redisTemplate.hasKey(CacheConstants.DRIVER_POINT_SETTING + dto.getDriverId());
            if(!b){
                continue;
            }
            //查询司机的配置
            DriverSettingRedisDto setting = (DriverSettingRedisDto)redisTemplate.opsForValue()
                    .get(CacheConstants.DRIVER_POINT_SETTING + dto.getDriverId());
            //判断司机最大接受里程数
            Integer orderDistance = setting.getOrderDistance();
            if(order.getExpectsMileage().intValue() > orderDistance){
                continue;
            }
            //司机没有开启接单
            if(!setting.getListenService()){
                continue;
            }
            //判断当前订单是否在司机的接受范围内
            if(dto.getValue()>setting.getRangeDistance()){
                continue;
            }
            driverIds.add(dto);
        }
        return driverIds;
    }

    /**
     * 给司机推送订单消息
     * @param drivers
     */
    private void pushOrderMessage(List<GeoDriverDto> drivers,Order order) {
        for (GeoDriverDto driver : drivers) {
            //构建发送消息的数据
            PushDriverOrderMsgDto pushDriverOrderMsgDto = new PushDriverOrderMsgDto();
            BeanUtils.copyProperties(order,pushDriverOrderMsgDto);
            pushDriverOrderMsgDto.setExpectsMileage(order.getExpectsMileage().toString());
            if(pushDriverOrderMsgDto.getFavourAmount() == null){
                pushDriverOrderMsgDto.setFavourAmount(new BigDecimal(RConst.Num.INT_0));
            }
            //做转换
            DecimalFormat df=new DecimalFormat("######.######");
            String result=df.format(driver.getValue());
            pushDriverOrderMsgDto.setToDriverMileage(result);//到司机的距离
            Message<PushDriverOrderMsgDto> message = MessageBuilder.withPayload(pushDriverOrderMsgDto).build();
            //发送到MQ里面
            rocketMQTemplate.syncSend(RocketMQConstants.PUSH_DRIVER_ORDER_TOPIC+":"
                            +String.format(RocketMQConstants.PUSH_DRIVER_ORDER_TAG,driver.getDriverId()),
                    message);
        }
    }


    /**
     * 计算订单明细
     * @param
     */
    private void initOrderBill(Order order) {
        OrderBill orderBill = new OrderBill();
        orderBill.setOrderId(order.getId());
        orderBill.setOrderNo(order.getOrderNo());
        orderBill.setCreateTime(new Date());
        JSONResult result = ruleStartRemoteApi.getRuleDto(LocalDateTime.now().getHour());
        Object data = result.getData();
        RuleDto ruleDto = JSONObject.parseObject(JSON.toJSONString(data),RuleDto.class);
        BeanUtils.copyProperties(ruleDto,orderBill);
        orderBillMapper.insert(orderBill);
    }

    private Order saveOrder(OrderDto orderDto,String no,Long loginId) {
        //请求的数据已经搞定
        Order order = OrderDto.orderDtoToOrder(orderDto);
        order.setOrderNo(no);
        order.setCustomerId(loginId);
        order.setCreateTime(new Date());
        order.setStatus(Constants.orderStatus.ORDER_STATUS_WAIT);
        //从stpUtil取出来的扩展参数是jsonObject
        Object extra = StpUtil.getExtra(Constants.CURRENT_LOGIN_KEY);
        //先转JSON字符串在转对象
        Login login = JSONObject.parseObject(extra.toString(), Login.class);
        order.setCustomerName(login.getName());
        order.setCustomerPhone(login.getPhone());
        order.setCustomerPhoto(login.getAvatar());

        //计算预计费用
        JSONResult valuation = ruleStartRemoteApi.valuation(orderDto.getExpectsMileage().toString());
        if(!valuation.isSuccess()){
            throw new GlobalException(ErrorCode.SERVICE_ERROR);
        }
        order.setExpectsOrderAmount(new BigDecimal(valuation.getData().toString()));
        super.save(order);//mybatisPlus执行添加后会自动返回id到对象里面
        return order;
    }

}
